/**
 * This class represents a UI panel for adjusting and storing the settings of the Twitter client.
 */
package org.hackystat.ui.hackerrific.client;

import java.util.Date;

import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * This class represents a UI panel for adjusting and storing the settings of the Twitter client.
 * @author David J. Nickles
 *
 */
public class SettingsPanel extends VerticalPanel implements FocusListener, ClickListener {
  /** {@literal} WAIT_IMAGE icon. */        
  public static final String WAIT_IMAGE = "images/wait-animated.gif"; 
  /** {@literal} USER_IMAGE user icon. */
  private static final String USER_IMAGE = "images/user.gif";
  /** {@literal} USER_INVALID user invalid event state. */        
  private static final String USER_INVALID = "images/user-warning.gif"; 
  /** {@literal} USER_VALID user valid event state. */  
  private static final String USER_VALID = "images/user-green.gif";   
  /** {@literal} PRCERRORNAME name for RPC errors. */  
  private static final String PRCERRORNAME = "RPC client error"; 
  /** {@literal} SETTINGSPANELSTYLE stylesheet class for labels. */  
  private static final String SETTINGSPANELSTYLE = "settingsPanel";  
  /** {@literal} SMALLLABELSTYLE stylesheet class for labels. */  
  private static final String SMALLLABELSTYLE = "gwt-Label-small";   
  /** {@literal} EXAMPLELABELSTYLE stylesheet class for examples. */  
  private static final String EXAMPLELABELSTYLE = "gwt-Label-example"; 
  /** {@literal} VALIDLABELSTYLE stylesheet class for valid results. */  
  private static final String VALIDLABELSTYLE = "gwt-Label-valid"; 
  /** {@literal} INVALIDLABELSTYLE stylesheet class for invalid results. */  
  private static final String INVALIDLABELSTYLE = "gwt-Label-invalid";

  private TextBox userTextBox;
  private PasswordTextBox passwordTextBox;
  private Label userLabel;
  private Label passwordLabel;
  private Label userPanelFeedback;
  private Image userPanelImage;
  private Date cookieExpires;
  private Boolean userStatus;
  private Boolean rpcBusy;
  private Boolean firstcall;
  private Button okButton;
  private ListBox refreshListBox;
  private ListBox soundListBox;
  private ListBox tweetsTypeListBox;
  private CheckBox soundCheckBox;
  
  /**
   * This method creates a new settings panel.
   */
  public SettingsPanel() {
    this.rpcBusy = Boolean.FALSE;
    this.userStatus = Boolean.FALSE;
    this.firstcall = Boolean.TRUE;
    this.cookieExpires = new Date((new Date()).getTime() + (1000L * 60L * 60L * 24L * 365L));
    
    setStyleName(SETTINGSPANELSTYLE);
    
    HorizontalPanel userColumns = new HorizontalPanel();
    this.userPanelImage = new Image(USER_IMAGE);
    userColumns.add(this.userPanelImage);
    VerticalPanel userCol2Rows = new VerticalPanel();
    Label userPanelTitle = new Label("User");
    userCol2Rows.add(userPanelTitle);
    this.userPanelFeedback = new Label("Set a valid host, before entering user credentials.");
    this.userPanelFeedback.setStyleName(EXAMPLELABELSTYLE);
    userCol2Rows.add(this.userPanelFeedback);
    userColumns.add(userCol2Rows);
    add(userColumns);

    VerticalPanel cell1row2n3 = new VerticalPanel();
    
    HorizontalPanel row2 = new HorizontalPanel();
    Label userLabel = new Label("Username:");
    row2.add(userLabel);
    this.userTextBox = new TextBox();
    String userCookie = Cookies.getCookie("user");
    if (userCookie == null) {
      Cookies.setCookie("user", this.userTextBox.getText());
    }
    else {
      this.userTextBox.setText(userCookie);
    }
    this.userTextBox.addFocusListener(this);
    row2.add(this.userTextBox);
    userLabel.setStyleName(SMALLLABELSTYLE);
    this.userLabel = new Label("Example: gmailusername");
    this.userLabel.setStyleName(EXAMPLELABELSTYLE);
    cell1row2n3.add(row2);
    cell1row2n3.add(this.userLabel);
    HorizontalPanel row3 = new HorizontalPanel();
    Label keyLabel = new Label("Password:");
    row3.add(keyLabel);
    this.passwordTextBox = new PasswordTextBox();
    String passwordCookie = Cookies.getCookie("password");
    if (passwordCookie == null) {
      Cookies.setCookie("password", this.passwordTextBox.getText());
    }
    else {
      this.passwordTextBox.setText(passwordCookie);
    }
    this.passwordTextBox.addFocusListener(this);
    row3.add(this.passwordTextBox);
    keyLabel.setStyleName(SMALLLABELSTYLE);
    this.passwordLabel = new Label("Example: MyVerySecretPassword");
    this.passwordLabel.setStyleName(EXAMPLELABELSTYLE);
    cell1row2n3.add(row3);
    cell1row2n3.add(this.passwordLabel);
    add(cell1row2n3);
    
    add(new HTML("<hr />"));
    
    HorizontalPanel tweetsFromStrip = new HorizontalPanel();
    Label tfLabel = new Label("Check for new tweets from");
    tfLabel.setStyleName(SMALLLABELSTYLE);
    tweetsFromStrip.add(tfLabel);
    this.tweetsTypeListBox = new ListBox();
    this.tweetsTypeListBox.addItem("your Friends list");
    this.tweetsTypeListBox.addItem("the Public list");
    this.tweetsTypeListBox.addItem("your Favorites list");
    this.tweetsTypeListBox.addItem("your Direct Messages list");
    this.tweetsTypeListBox.addItem("your list");
    this.tweetsTypeListBox.setVisibleItemCount(1);
    String tweetFromCookie = Cookies.getCookie("source");
    if (tweetFromCookie == null) {
      Cookies.setCookie("source", Integer.toString(this.tweetsTypeListBox.getSelectedIndex()));
    }
    else {
      this.tweetsTypeListBox.setSelectedIndex(Integer.parseInt(tweetFromCookie));
    }
    this.tweetsTypeListBox.addClickListener(this);
    tweetsFromStrip.add(this.tweetsTypeListBox);
    add(tweetsFromStrip);
    
    HorizontalPanel refreshStrip = new HorizontalPanel();
    Label rfLabel = new Label("Automatically refresh tweets");
    rfLabel.setStyleName(SMALLLABELSTYLE);
    refreshStrip.add(rfLabel);   
    this.refreshListBox = new ListBox();
    this.refreshListBox.addItem("1");
    this.refreshListBox.addItem("5");
    this.refreshListBox.addItem("15");
    this.refreshListBox.addItem("30");
    this.refreshListBox.addItem("60");
    this.refreshListBox.setVisibleItemCount(1);
    String refreshCookie = Cookies.getCookie("refresh");
    if (refreshCookie == null) {
      Cookies.setCookie("refresh", Integer.toString(this.refreshListBox.getSelectedIndex()));
    }
    else {
      this.refreshListBox.setSelectedIndex(Integer.parseInt(refreshCookie));
    }
    this.refreshListBox.addClickListener(this);
    refreshStrip.add(this.refreshListBox);
    Label rfLabel2 = new Label("minutes");
    rfLabel2.setStyleName(SMALLLABELSTYLE);
    refreshStrip.add(rfLabel2);   
    add(refreshStrip);
    
    HorizontalPanel soundsStrip = new HorizontalPanel();
    this.soundCheckBox = new CheckBox("Play Notification Sound");
    this.soundCheckBox.setStyleName(SMALLLABELSTYLE);
    String enableSoundCookie = Cookies.getCookie("mute");
    if (enableSoundCookie == null) {
      Cookies.setCookie("mute", this.soundCheckBox.isChecked() ? 
          Boolean.FALSE.toString() : Boolean.TRUE.toString());
    }
    else {
      soundCheckBox.setChecked(Boolean.TRUE.toString().equals(enableSoundCookie));
    }
    soundsStrip.add(this.soundCheckBox);
    this.soundCheckBox.addClickListener(this);
    this.soundListBox = new ListBox();
    this.soundListBox.addItem("Orignal");
    this.soundListBox.addItem("Cardinal1");
    this.soundListBox.addItem("Cardinal2");
    this.soundListBox.addItem("Robin");
    this.soundListBox.addItem("Random Choice");
    this.soundListBox.setVisibleItemCount(1);
    String chirpCookie = Cookies.getCookie("chirp");
    if (chirpCookie == null) {
      Cookies.setCookie("chirp", Integer.toString(this.soundListBox.getSelectedIndex()));
    }
    else {
      this.soundListBox.setSelectedIndex(Integer.parseInt(chirpCookie));
    }
    soundsStrip.add(this.soundListBox);
    this.soundListBox.addClickListener(this);
    add(soundsStrip);
    this.soundCheckBox.setEnabled(false);
    this.soundListBox.setEnabled(false);
    
    add(this.okButton = new Button("OK"));
    
    if ((this.userTextBox.getText().length() > 0) 
      && (this.passwordTextBox.getText().length() > 0)) {
      rpcAuthenticate(this.userTextBox.getText(), this.passwordTextBox.getText());
    }
  }
  
  /**
   * Nothing happens when the settings fields are given focus.
   * @param sender the UI widget that was given focus
   */
  public void onFocus(Widget sender) {
    /* nothing to do here */
  }
  
  /**
   * This method gets the user name that was entered into the UI.
   * @return the user name.
   */
  public String getUser() {
    return this.userTextBox.getText();
  }
  
  /**
   * This method gets the password that was entered into the UI.
   * @return the password.
   */
  public String getPassword() {
    return this.passwordTextBox.getText();
  }
  
  /**
   * This method sets the correct panel Images from the host and user status.
   */
  private void displayStatus() {
    this.rpcBusy = Boolean.FALSE;
    if (this.userStatus.booleanValue()) {
      this.userPanelImage.setUrl(USER_VALID);
      this.userPanelFeedback.setText("User " + this.userTextBox.getText() + " authorized.");
      this.userPanelFeedback.setStyleName(VALIDLABELSTYLE);
      if (this.firstcall.booleanValue()) {
        this.firstcall = Boolean.FALSE;
        // trigger a click on the 'ok' button.
      }
    } 
    else {
      this.userPanelImage.setUrl(USER_INVALID);
      this.userPanelFeedback.setText("User " + this.userTextBox.getText() 
        + " authorization failed.");
      this.userPanelFeedback.setStyleName(INVALIDLABELSTYLE);
    }
  }
  
  /**
   * This method sets the state of the user & key validation.
   * @param state Boolean represents the state of the user valid=true, invalid=false
   */
  private void setUserStatus(Boolean state) {
    if (state.booleanValue()) {
      this.userStatus = Boolean.TRUE;
    } 
    else {
      this.userStatus = Boolean.FALSE;
    }
    displayStatus();
  }

  /**
   * Returns an HTTP 200 OK response code and a format-specific response if authentication
   * was successful.  Use this method to test if supplied user credentials are valid with 
   * minimal overhead.
   * @param user user to authentication
   * @param password user password for authentication
   */
  private void rpcAuthenticate(String user, String password) {
    if (!this.rpcBusy.booleanValue()) {
      this.rpcBusy = Boolean.TRUE;
      this.userPanelImage.setUrl(WAIT_IMAGE);
      RPCServiceFactory.getInstance().getService().authenticate(user, password,
        new AsyncCallback()
        {
          public void onSuccess(Object result) {
            setUserStatus((Boolean) result);
          }

          public void onFailure(Throwable caught) {
            if (caught instanceof com.google.gwt.user.client.rpc.InvocationException) {
              log(PRCERRORNAME, "Cannot invoke method on servlet.");
            } 
            else {
              log(PRCERRORNAME, caught.toString());
            }
          }
        });
    }
  }

  /** 
   * When any of the settings fields lose focus then check to see if the information is valid.
   * @param sender the UI widget that is losing focus.
   */
  public void onLostFocus(Widget sender) {
    if ((sender.equals(this.userTextBox)) || (sender.equals(this.passwordTextBox))) {
      Cookies.setCookie("user", this.userTextBox.getText(), this.cookieExpires);
      Cookies.setCookie("password", this.passwordTextBox.getText(), this.cookieExpires);
      rpcAuthenticate(this.userTextBox.getText(), this.passwordTextBox.getText());
    }
  }
  
  /**
   * Check if user is valid.
   * @return true if the credentials have been validated with the server. 
   */
  public Boolean userValid() {
    return this.userStatus;
  }
  
  /**
   * Log this message in the display.
   * @param header the message header or subject
   * @param body the message body
   */
  private void log(String header, String body) {
    Window.alert(header + " : " + body);
  }
  
  /**
   * Returns a reference to the 'OK' button which closes this panel.
   * 
   * @return the UI item for flipping to the statuses view.
   */
  public Button getOkButton() {
    return this.okButton;
  }

  /** Respond to user clicks.
   * @param sender the widget clicked on.
   * @see com.google.gwt.user.client.ui.ClickListener#onClick(com.google.gwt.user.client.ui.Widget)
   */
  public void onClick(Widget sender) {
    if (sender == this.tweetsTypeListBox) {
      Cookies.setCookie("source", Integer.toString(this.tweetsTypeListBox.getSelectedIndex()));
    }
    else if (sender == this.refreshListBox) {
      Cookies.setCookie("refresh", Integer.toString(this.refreshListBox.getSelectedIndex()));
    }
    else if (sender == this.soundCheckBox) {
      Cookies.setCookie("mute", this.soundCheckBox.isChecked() ? 
        Boolean.FALSE.toString() : Boolean.TRUE.toString());
    }
    else if (sender == this.soundListBox) {
      Cookies.setCookie("chirp", Integer.toString(this.soundListBox.getSelectedIndex()));
    }
  }
}
